package com.ma.springframework.beans.factory.support;

import com.ma.springframework.beans.BeansException;
import com.ma.springframework.beans.factory.BeanFactory;
import com.ma.springframework.beans.factory.FactoryBean;
import com.ma.springframework.beans.factory.config.BeanDefinition;
import com.ma.springframework.beans.factory.config.BeanPostProcessor;
import com.ma.springframework.beans.factory.config.ConfigrableBeanFactory;
import com.ma.springframework.util.ClassUtils;

import java.util.ArrayList;
import java.util.List;

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigrableBeanFactory {
    private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<BeanPostProcessor>();
    private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();


    public ClassLoader getBeanClassLoader() {
        return this.beanClassLoader;
    }

    @Override
    public Object getBean(String name) throws BeansException {
        return doGetBean(name,null);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return doGetBean(name, args);
    }
    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return (T) getBean(name);
    }

    private <T> T doGetBean(String name, Object[] args) {
        Object sharedInstance  = getSingleton(name);
        if (sharedInstance  != null){
            return (T) getObjectForBeanInstance(sharedInstance ,name);
        }
        BeanDefinition beanDefinition = getBeanDefinition(name);
        Object bean = createBean(name, beanDefinition, args);
        return (T)getObjectForBeanInstance(beanDefinition,name);
    }

    private Object getObjectForBeanInstance(Object bean, String name) {
         if (!(bean instanceof FactoryBean)){
             return bean;
         }
         Object object = getCacheObjectForFactoryBean(name);
         if (object == null){
             FactoryBean<?> factoryBean =(FactoryBean<?>) bean;
             object =getObjectFromFactoryBean(factoryBean,name);
         }
         return object;
    }

    protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

    protected abstract Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException;
    @Override
    public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor){
        this.beanPostProcessors.remove(beanPostProcessor);
        this.beanPostProcessors.add(beanPostProcessor);
    }

    /**
     * Return the list of BeanPostProcessors that will get applied
     * to beans created with this factory.
     */
    public List<BeanPostProcessor> getBeanPostProcessors() {
        return this.beanPostProcessors;
    }

}
